package me.helllp.bootman.back.services.impl;

import java.security.Key;
import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import me.helllp.bootman.back.config.shiro.JwtPlayload;
import me.helllp.bootman.back.services.TokenService;

/**
 * 基于JWT的Token签发和验证
 * 
 * @author Administrator
 *
 */
@SuppressWarnings("restriction")
@Service
public class TokenServiceJwtImpl implements TokenService{
	/**
	 * JWT 秘钥
	 */
	@Value("${bootman.tokenservice.jwt.secretkey}")
	private static String SECRETKEY = "*okdkhos97##$)902-BMSf41585fdasf";
	
	private static final String ROLES = TokenServiceJwtImpl.class.getName() + "_roles";
	
	private static final String PERMS = TokenServiceJwtImpl.class.getName() + "_perms";
	
	@Override
	public String signToken(String tokenId, Long userId, String issuer, Long period, String roles, String permissions) {
        JwtBuilder jwt  =  Jwts.builder();
        
        if(StringUtils.isNotBlank(tokenId)){
        	jwt.setId(tokenId);
        }
        
        // 	用户名主题
        jwt.setSubject("" + userId);
        
        //	签发者
        if(StringUtils.isNotBlank(issuer)){
        	jwt.setIssuer(issuer);
        }

		//	获取时间戳
        long currentTimeMillis = System.currentTimeMillis();
        
        //	签发时间
        jwt.setIssuedAt(new Date(currentTimeMillis));
        
        if(null != period){
            Date expiration = new Date(currentTimeMillis+period);
            //	有效时间
            jwt.setExpiration(expiration);
        }
        
        //	角色
        if(StringUtils.isNotBlank(roles)){
        	jwt.claim(TokenServiceJwtImpl.ROLES, roles);
        }
        //	权限
        if(StringUtils.isNotBlank(permissions)){
        	jwt.claim(TokenServiceJwtImpl.PERMS, permissions);
        }
        //	压缩，可选GZIP
        jwt.compressWith(CompressionCodecs.DEFLATE);
        
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        
		byte[] secretKeyBytes = DatatypeConverter.parseBase64Binary(SECRETKEY);
        Key signingKey = new SecretKeySpec(secretKeyBytes, signatureAlgorithm.getJcaName());
        //	加密设置
        jwt.signWith(signatureAlgorithm, signingKey);
        
        return jwt.compact();
	}

	@Override
	public JwtPlayload verifyToken(String token) throws AuthenticationException {
		JwtPlayload jwtPlayload;
		try {
			Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(SECRETKEY))
					.parseClaimsJws(token).getBody();
			jwtPlayload = new JwtPlayload();
			// TokenID
			jwtPlayload.setId(claims.getId());
			// 用户ID
			jwtPlayload.setUserId(claims.getSubject());
			// 签发者
			jwtPlayload.setIssuer(claims.getIssuer());
			// 签发时间
			jwtPlayload.setIssuedAt(claims.getIssuedAt());
			// 接收方
			jwtPlayload.setAudience(claims.getAudience());
			// 访问主张-角色
			jwtPlayload.setRoles(claims.get(TokenServiceJwtImpl.ROLES, String.class));
			// 访问主张-权限
			jwtPlayload.setPerms(claims.get(TokenServiceJwtImpl.PERMS, String.class));
		} catch (ExpiredJwtException e) {
			throw new AuthenticationException("JWT 令牌过期");
		} catch (UnsupportedJwtException e) {
			throw new AuthenticationException("JWT 令牌无效");
		} catch (MalformedJwtException e) {
			throw new AuthenticationException("JWT 令牌格式错误");
		} catch (SignatureException e) {
			throw new AuthenticationException("JWT 令牌签名无效");
		} catch (IllegalArgumentException e) {
			throw new AuthenticationException("JWT 令牌参数异常");
		} catch (Exception e) {
			throw new AuthenticationException("JWT 令牌错误");
		}

		return jwtPlayload;
	}

}
